﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Net;
using BusinessObjects;
using System.Xml.Serialization;
using System.Diagnostics;
using System.Threading;


namespace YourWorldServer
{
    public class SocketServer
    {
        private int bufferSize = 1024; //10240 * 10;
        private TcpListener tcpListener;
        private TcpClient tcpClient;
        private bool isRemoveInactiveInProgress = false;

        private List<StreamWriter> clientConnections;
        private Dictionary<int, List<PlayerStream>> mapPlayers;

        private Timer removeInActivePlayersTimer;

        public string WebServicesLocation
        {
            get;
            set;
        }

        public static bool IsStarted
        {
            get;
            set;
        }

        public SocketServer( string serviceUrl )
        {
            try
            {
                clientConnections = new List<StreamWriter>();
                this.mapPlayers = new Dictionary<int, List<PlayerStream>>();

                this.WebServicesLocation = serviceUrl;

                GameManager.ServiceUrl = this.WebServicesLocation;
                RegionObjectsRepository.ServiceUrl = this.WebServicesLocation;
                RubbishBinsRepository.ServiceUrl = this.WebServicesLocation;

                this.removeInActivePlayersTimer = new Timer( new TimerCallback( this.WaitForPingResponses ) );

                GameManager.Instance.GameInitializationCompleted = this.GameInicializationCompleted;
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Initialization Error: " + e.Message );
            }
        }

        public void StartSocketServer()
        {
            try
            {
                Console.WriteLine( " --- Starting the server..." );
                tcpListener = new TcpListener( IPAddress.Any, 4531 );
                tcpListener.Start();

                tcpListener.BeginAcceptTcpClient( this.OnBeginAcceptTcpClient, null );
                IsStarted = true;
                Console.WriteLine( " --- Server started successfully." );
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Error starting the server: " + e.Message );
                this.StopSocketServer();
            }
        }

        public void StartSocketServer( IPEndPoint ipEndPoint )
        {
            try
            {
                //Console.WriteLine( " --- Starting the server..." );
                tcpListener = new TcpListener( ipEndPoint );
                tcpListener.Start();
                
                tcpListener.BeginAcceptTcpClient( this.OnBeginAcceptTcpClient, null );
                IsStarted = true;
                //Console.WriteLine( " --- Server started successfully." );
            }
            catch ( Exception e )
            {
                //Console.WriteLine( "Error starting the server: " + e.Message );
                this.StopSocketServer();
            }
        }

        public void StopSocketServer()
        {
            Console.WriteLine( " --- Stopping the server..." );
            try
            {
                if ( this.clientConnections != null )
                {
                    foreach ( var streamWriter in this.clientConnections )
                        streamWriter.Close();
                }
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Error closing the sockets server: " + e.Message );
            }

            try
            {
                if ( tcpListener != null )
                    tcpListener.Stop();
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Error closing the sockets server: " + e.Message );
            }

            try
            {
                if ( tcpClient != null )
                    tcpClient.Close();
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Error closing the sockets server: " + e.Message );
            }

            Console.WriteLine( " --- Stop server succeeded." );
            IsStarted = false;
        }

        public void OnBeginAcceptTcpClient( IAsyncResult ar )
        {
            try
            {
                // Console.WriteLine( "Client connected successfully" );
                tcpClient = tcpListener.EndAcceptTcpClient( ar );
                                
                StreamWriter streamWriter = new StreamWriter( tcpClient.GetStream() );
                
                byte[] buffer = new byte[bufferSize];
                AsyncCallback ac = new AsyncCallback( MessageReceived );

                Payload payload = new Payload()
                {
                    Buffer = buffer,
                    Stream = streamWriter.BaseStream
                };

                streamWriter.AutoFlush = true;              
                streamWriter.BaseStream.BeginRead( buffer, 0, bufferSize, ac, payload );

                clientConnections.Add( streamWriter );
                streamWriter.AutoFlush = true;

                //wait again for new connection
                tcpListener.BeginAcceptTcpClient( OnBeginAcceptTcpClient, null );
            }
            catch ( Exception e )
            {
                Console.WriteLine( "Connection Error, please restart the server: " + e.Message );
                this.StopSocketServer();
            }
        }

        private void MessageReceived( IAsyncResult result )
        {
            try
            {
                Payload payload = result.AsyncState as Payload;
                int bytesRead = payload.Stream.EndRead( result );

                if ( payload == null )
                    throw new ArgumentException( "The message payload should not be null!" );

                if ( bytesRead < 1 )
                {
                    this.PingAllPlayers();
                    return;
                }

                switch ( payload.Buffer[0] )
                {
                    case ( byte )SocketMessageKeys.Subscribe:
                        this.PlayerSubscribed( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.MoveToPosition:
                        this.MoveToPosition( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.UnSubscribe:
                        this.Unsubscribe( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.BinsStatusUpdate:
                        this.BinsStatusUpdate( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.StartEvent:
                        EventsManager.Instance.StartAnEvent( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.SupportEventResponse:
                        EventsManager.Instance.ReceiveEventFeedBack( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.JoinEventResponse:
                        EventsManager.Instance.ReceiveEventFeedBack( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.Chat:
                        PalyersCommunicator.Instance.ChatMessageReceived( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.PlayerConnectionRequest:
                        PalyersCommunicator.Instance.PlayerConnectionRequest( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.PlayerConnectionResponse:
                        PalyersCommunicator.Instance.PlayerConnectionResponse( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.BuildQuestUpdate:
                        EventsManager.Instance.BuildQuestUpdate( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.BadgeWonUpdate:
                        EventsManager.Instance.BadgeWonUpdate( payload, bytesRead );
                        break;

                    case ( byte )SocketMessageKeys.PingResponse:
                        this.PingResponseReceived( payload, bytesRead );
                        break;
                }

                AsyncCallback ac = new AsyncCallback( MessageReceived );
                payload.Stream.BeginRead( payload.Buffer, 0, 10240, ac, payload );
            }
            catch ( Exception e )
            {
                SocketException socketError = e.InnerException as SocketException;
                if ( socketError != null )
                {
                    // do nothing - someone has shut down his/hers browser...
                    this.PingAllPlayers();
                    Console.WriteLine( "Socket Error: " + socketError.SocketErrorCode );
                }
                else
                {
                    Console.WriteLine( "Connection Error, please restart the server: " + e.Message );
                    this.StopSocketServer();
                }
            }
        }

        private void PlayerSubscribed( Payload payload, int bytesRead )
        {
            try
            {
                //Console.WriteLine( "Subscribing Player..." );

                string xmlData = Encoding.UTF8.GetString( payload.Buffer, 1, bytesRead );
                XmlSerializer xmlSrl = new XmlSerializer( typeof( Player ) );
                StringReader stream = new StringReader( xmlData );
                StringWriter textWriter;
                string xml;
                byte[] xmlMessage;
                byte[] message;

                Player player = xmlSrl.Deserialize( stream ) as Player;

                if ( GameManager.Instance.Players.Keys.Count == 0 )
                    player.Id = 1;
                else
                    player.Id = GameManager.Instance.Players.Keys.Max() + 1; // we want non-zero Ids

                int realmapId = 1; //we have only one map in the DB :D
                if ( player.MapId > 100 ) // single player maps are mapId + 100
                {
                    player.MapId = player.Id + 100;
                }

                Game game = GameManager.Instance.GetGame( player.MapId );
                if ( game == null )
                {
                    GameManager.Instance.InitializeNewGame( player.MapId, realmapId );
                }

                if ( game != null )
                {
                    foreach ( Player existingMapPlayer in game.Players )
                        existingMapPlayer.IsMainPlayer = false;

                    player.IsMainPlayer = true;
                    game.Players.Insert( 0, player );

                    //Get the Game in bytes
                    XmlSerializer xmlGameSrl = new XmlSerializer( typeof( Game ) );
                    textWriter = new StringWriter();

                    xmlGameSrl.Serialize( textWriter, game );
                    xml = textWriter.ToString();

                    xmlMessage = Encoding.UTF8.GetBytes( xml );
                    message = new byte[xmlMessage.Length + 1];

                    message[0] = ( byte )SocketMessageKeys.GameInicialization;
                    xmlMessage.CopyTo( message, 1 );

                    //send:                    
                    payload.Stream.Write( message, 0, message.Length );
                }

                if ( this.mapPlayers.ContainsKey( player.MapId ) == false )
                    this.mapPlayers.Add( player.MapId, new List<PlayerStream>() );

                textWriter = new StringWriter();

                xmlSrl.Serialize( textWriter, player );
                xml = textWriter.ToString();

                xmlMessage = Encoding.UTF8.GetBytes( xml );
                message = new byte[xmlMessage.Length + 1];

                message[0] = ( byte )SocketMessageKeys.Subscribe;
                xmlMessage.CopyTo( message, 1 );

                foreach ( PlayerStream ps in this.mapPlayers[player.MapId] )
                {
                    Debug.Assert( ps.Stream != null );
                    ps.Stream.Write( message, 0, message.Length );
                }

                PlayerStream gamePlayer = new PlayerStream()
                {
                    Player = player,
                    Stream = payload.Stream
                };

                this.mapPlayers[player.MapId].Add( gamePlayer );
                GameManager.Instance.AddGamePlayer( gamePlayer );

               // Console.WriteLine( "Player Subscribed." );
            }
            catch ( Exception e )
            {
               // Console.WriteLine( "Subscribing Player Error: " + e.Message );
                this.StopSocketServer();
            }
        }

        private void GameInicializationCompleted( Game game )
        {
            try
            {
               // Console.WriteLine( "Initializing Game..." );

                if ( this.mapPlayers.ContainsKey( game.MapId ) == false )
                    return;

                PlayerStream ps = this.mapPlayers[game.MapId][0];
                ps.Player.IsMainPlayer = true;
                game.Players.Add( ps.Player );

                //Get the Game in bytes
                XmlSerializer xmlGameSrl = new XmlSerializer( typeof( Game ) );
                StringWriter textWriter = new StringWriter();

                xmlGameSrl.Serialize( textWriter, game );
                string xml = textWriter.ToString();

                byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
                byte[] message = new byte[xmlMessage.Length + 1];

                message[0] = ( byte )SocketMessageKeys.GameInicialization;
                xmlMessage.CopyTo( message, 1 );

                try
                {
                    StringReader stream = new StringReader( xml );
                    Game test = xmlGameSrl.Deserialize( stream ) as Game;
                }
                catch ( Exception ee )
                {
                    int debug = 1;
                }

                //send:
                ps.Stream.Write( message, 0, message.Length );

              //  Console.WriteLine( "Game Initialized." );
            }
            catch ( Exception e )
            {
             //   Console.WriteLine( "Game Inicialization Error: " + e.Message );
                this.StopSocketServer();
            }
        }

        private void MoveToPosition( Payload payload, int bytesRead )
        {
            try
            {
                string xmlData = Encoding.UTF8.GetString( payload.Buffer, 1, bytesRead - 1 );
                XmlSerializer xmlSrl = new XmlSerializer( typeof( MoveMessage ) );
                StringReader stream = new StringReader( xmlData );

                MoveMessage moveMessage = xmlSrl.Deserialize( stream ) as MoveMessage;
                int playerMapId = 0;

                foreach ( int mapId in this.mapPlayers.Keys )
                {
                    if ( this.mapPlayers[mapId].Count( c => c.Player.Id == moveMessage.PlayerId ) == 1 )
                    {
                        playerMapId = mapId;
                        break;
                    }
                }

                if ( playerMapId == 0 )
                {
                    Console.WriteLine( "Players got messy... " );
                    return;
                }

                Game game = GameManager.Instance.GetGame( playerMapId );

                //Refresh the value locally:
                Player player = game.Players.Where( obj => obj.Id == moveMessage.PlayerId ).FirstOrDefault() as Player;

                player.Y = moveMessage.Y;
                player.X = moveMessage.X;

                //switch ( moveMessage.MoveDirection )
                //{
                //    case MoveDirection.Down: player.Y -= Consts.PlayerStep; break;
                //    case MoveDirection.Up: player.Y += Consts.PlayerStep; break;
                //    case MoveDirection.Left: player.X -= Consts.PlayerStep; break;
                //    case MoveDirection.Right: player.X += Consts.PlayerStep; break;
                //}

                //check for rubbishes and load them in the message's Payload
                int gridx = game.Grid.Count - ( int )( player.Y / Consts.MapTileWidth ) - 1;
                int gridy = ( int )( player.X / Consts.MapTileHeight );

                if ( gridx >= 0 && gridy >= 0 && gridx < game.Grid.Count && gridy < game.Grid[gridx].Count )
                {
                    int objectId = game.Grid[gridx][gridy];
                    GameObject go = objectId > 0 ? RegionObjectsRepository.Instance.GetRubbinshForId( objectId ) : null;
                    if ( go != null )
                    {
                        go.GridX = gridx;
                        go.GridY = gridy;
                        moveMessage.Payload = go;
                        game.Grid[gridx][gridy] = 0; // mark the object as being looked by someone, so that other palyes cannot grab it at the same time
                    }
                }

                //send to all the players on the map:
                StringWriter textWrite = new StringWriter();

                xmlSrl.Serialize( textWrite, moveMessage );
                string xml = textWrite.ToString();

                byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
                byte[] messageToSend = new byte[xmlMessage.Length + 1];

                messageToSend[0] = ( byte )SocketMessageKeys.MoveToPosition;
                xmlMessage.CopyTo( messageToSend, 1 );

                foreach ( PlayerStream ps in this.mapPlayers[playerMapId] )
                {
                    Debug.Assert( ps.Stream != null );
                    ps.Stream.Write( messageToSend, 0, messageToSend.Length );
                }
            }
            catch ( Exception exception )
            {
                Console.WriteLine( "Player Move To Position Command Error: " + exception.Message );
                this.PingAllPlayers();
                // this.StopSocketServer();
            }
        }

        private void Unsubscribe( Payload payload, int bytesRead )
        {
            try
            {
                string xmlData = Encoding.UTF8.GetString( payload.Buffer, 1, bytesRead - 1 );
                XmlSerializer xmlSrl = new XmlSerializer( typeof( Player ) );
                StringReader stream = new StringReader( xmlData );

                Player player = xmlSrl.Deserialize( stream ) as Player;

                int id = player.Id;
                Console.WriteLine( "Unsubscribing Player with id = " + id );

                int playerMapId = player.MapId;
                PlayerStream playerStream;

                playerStream = this.mapPlayers[playerMapId].Where( c => c.Player.Id == id ).FirstOrDefault();
                if ( playerStream == null )
                {
                    Console.WriteLine( "PlayerStream not found!" );
                    return;
                }

                Game game = GameManager.Instance.GetGame( playerMapId );
                game.Players.Remove( playerStream.Player );
                GameManager.Instance.Players.Remove( player.Id );

                foreach ( PlayerStream ps in this.mapPlayers[playerMapId] )
                    ps.Stream.Write( payload.Buffer, 0, bytesRead );

                this.mapPlayers[playerMapId].Remove( playerStream );
                Console.WriteLine( "Removing player with id - " + id );
                Console.WriteLine( "Player Unsubscribed." );
            }
            catch ( Exception exception )
            {
                Console.WriteLine( "Player Unsubscribe Error: " + exception.Message );
                this.StopSocketServer();
            }
        }

        private void BinsStatusUpdate( Payload payload, int bytesRead )
        {
            try
            {
                string xmlData = Encoding.UTF8.GetString( payload.Buffer, 1, bytesRead - 1 );
                XmlSerializer xmlSrl = new XmlSerializer( typeof( BinsUpdateMessage ) );
                StringReader stream = new StringReader( xmlData );

                BinsUpdateMessage binsMessage = xmlSrl.Deserialize( stream ) as BinsUpdateMessage;
                int playerMapId = 0;

                foreach ( int mapId in this.mapPlayers.Keys )
                {
                    if ( this.mapPlayers[mapId].Count( c => c.Player.Id == binsMessage.PlayerId ) == 1 )
                    {
                        playerMapId = mapId;
                        break;
                    }
                }

                Game game = GameManager.Instance.GetGame( playerMapId );

                switch ( binsMessage.MessageStatus )
                {
                    case RubbishRemovalStatus.UpdateServer:

                        // identify
                        GameObject rubbish = RegionObjectsRepository.Instance.GetRubbinshForId( binsMessage.RubbishId );
                        rubbish.GridX = binsMessage.RubbishGridX;
                        rubbish.GridY = binsMessage.RubbishGridY;

                        // add to bin locally
                        RubbishBinsRepository.Instance.AddRubbsihToBin( rubbish as Rubbish, binsMessage.MaterialTypeId );

                        //remove from map locally
                        game.Grid[rubbish.GridX][rubbish.GridY] = 0;
                        RegionObjectHolder rubbishHolder = new RegionObjectHolder( rubbish.Id, rubbish.GridX, rubbish.GridY );
                        game.SimplifiedGameObjects.Remove( rubbishHolder );

                        // add more rubbish to the grid if needed
                        if ( game.SimplifiedGameObjects.Count <= Consts.MinRubbishCount )
                            this.RubbishMapUpdate( playerMapId, GameManager.Instance.GenerateRubbish( game, Consts.UpdateRubbishCount ) );

                        // send to others
                        binsMessage.MessageStatus = RubbishRemovalStatus.Remove;

                        StringWriter textWrite = new StringWriter();

                        xmlSrl.Serialize( textWrite, binsMessage );
                        string xml = textWrite.ToString();

                        byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
                        byte[] messageToSend = new byte[xmlMessage.Length + 1];

                        messageToSend[0] = ( byte )SocketMessageKeys.BinsStatusUpdate;
                        xmlMessage.CopyTo( messageToSend, 1 );

                        foreach ( PlayerStream ps in this.mapPlayers[playerMapId] )
                        {
                            Debug.Assert( ps.Stream != null );

                            // Inform all except the sender of the message
                            if ( ps.Player.Id != binsMessage.PlayerId )
                                ps.Stream.Write( messageToSend, 0, messageToSend.Length );
                        }

                        break;

                    case RubbishRemovalStatus.Unlock:
                        //Tell all the players on the map that this player has decided to take 
                        //no action with this rubbish and they should make it enabled again :)

                        game.Grid[binsMessage.RubbishGridX][binsMessage.RubbishGridY] = binsMessage.RubbishId;

                        foreach ( PlayerStream ps in this.mapPlayers[playerMapId] )
                        {
                            Debug.Assert( ps.Stream != null );

                            // Inform all except the sender of the message
                            if ( ps.Player.Id != binsMessage.PlayerId )
                                ps.Stream.Write( payload.Buffer, 0, bytesRead );
                        }

                        break;
                }
            }
            catch ( Exception exception )
            {
                Console.WriteLine( "Recycling Bins Status Update Error: " + exception.Message );
                this.StopSocketServer();
            }
        }

        private void RubbishMapUpdate( int playerMapId, List<RegionObjectHolder> rubbishObjects )
        {
            StringWriter textWrite = new StringWriter();
            XmlSerializer xmlSrl = new XmlSerializer( typeof( List<RegionObjectHolder> ) );
            xmlSrl.Serialize( textWrite, rubbishObjects );
            string xml = textWrite.ToString();

            byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
            byte[] messageToSend = new byte[xmlMessage.Length + 1];

            messageToSend[0] = ( byte )SocketMessageKeys.RubbishUpdate;
            xmlMessage.CopyTo( messageToSend, 1 );

            foreach ( PlayerStream ps in this.mapPlayers[playerMapId] )
            {
                Debug.Assert( ps.Stream != null );

                // Inform all
                ps.Stream.Write( messageToSend, 0, messageToSend.Length );
            }
        }

        private void ExecuteRemoveInactivePlayers()
        {
            List<PlayerStream> inactivePlayers = new List<PlayerStream>();
            PlayerStream playerStream;

            foreach ( int playerId in GameManager.Instance.Players.Keys )
            {
                playerStream = GameManager.Instance.Players[playerId];

                if ( playerStream.IsWaitingPingResponse == true )
                    inactivePlayers.Add( playerStream );
            }

            foreach ( PlayerStream ps in inactivePlayers )
            {
                Player player = ps.Player;
                GameManager.Instance.Players.Remove( player.Id );

                Game playerMap = GameManager.Instance.GetGame( player.MapId );
                playerMap.Players.Remove( player );

                if ( this.mapPlayers.ContainsKey( player.MapId ) == true )
                    this.mapPlayers[player.MapId].Remove( ps );

                XmlSerializer xmlSrl = new XmlSerializer( typeof( Player ) );
                StringWriter textWrite = new StringWriter();

                xmlSrl.Serialize( textWrite, player );
                string xml = textWrite.ToString();

                byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
                byte[] message = new byte[xmlMessage.Length + 1];

                message[0] = ( byte )SocketMessageKeys.UnSubscribe;
                xmlMessage.CopyTo( message, 1 );

                foreach ( PlayerStream pStream in this.mapPlayers[player.MapId] )
                {
                    if ( pStream.IsWaitingPingResponse == false )
                        pStream.Stream.Write( message, 0, message.Length );
                }
            }
        }

        private void PingAllPlayers()
        {
            if ( this.isRemoveInactiveInProgress == true )
                return;

            byte[] messageToSend = new byte[2];
            messageToSend[0] = ( byte )SocketMessageKeys.Ping;

            List<PlayerStream> inactivePlayers = new List<PlayerStream>();
            PlayerStream playerStream;

            foreach ( int playerId in GameManager.Instance.Players.Keys )
            {
                messageToSend[1] = Convert.ToByte( playerId );
                playerStream = GameManager.Instance.Players[playerId];

                try
                {
                    playerStream.Stream.Write( messageToSend, 0, messageToSend.Length );
                    playerStream.Stream.Flush();

                    playerStream.IsWaitingPingResponse = true;
                }
                catch ( IOException e )
                {
                    SocketException socketError = e.InnerException as SocketException;

                    if ( socketError != null &&
                        ( socketError.SocketErrorCode == SocketError.ConnectionReset ||
                        socketError.SocketErrorCode == SocketError.ConnectionAborted ||
                        socketError.SocketErrorCode == SocketError.ConnectionRefused ) )
                    {
                        inactivePlayers.Add( playerStream );
                    }
                }
            }

            foreach ( PlayerStream ps in inactivePlayers )
            {
                Player player = ps.Player;

                GameManager.Instance.Players.Remove( player.Id );

                Game playerMap = GameManager.Instance.GetGame( player.MapId );
                playerMap.Players.Remove( player );

                if ( this.mapPlayers.ContainsKey( player.MapId ) == true )
                    this.mapPlayers[player.MapId].Remove( ps );

                XmlSerializer xmlSrl = new XmlSerializer( typeof( Player ) );
                StringWriter textWrite = new StringWriter();

                xmlSrl.Serialize( textWrite, player );
                string xml = textWrite.ToString();

                byte[] xmlMessage = Encoding.UTF8.GetBytes( xml );
                byte[] message = new byte[xmlMessage.Length + 1];

                message[0] = ( byte )SocketMessageKeys.UnSubscribe;
                xmlMessage.CopyTo( message, 1 );

                foreach ( PlayerStream pStream in this.mapPlayers[player.MapId] )
                    pStream.Stream.Write( message, 0, message.Length );
            }

            if ( this.isRemoveInactiveInProgress == false )
            {
                this.isRemoveInactiveInProgress = true;
                Thread t = new Thread( new ThreadStart( this.WaitForPingResponses ) );
                t.Start();
                //this.removeInActivePlayersTimer.Change( 0, 5000 );
            }
        }

        private void PingResponseReceived( Payload payload, int bytesRead )
        {
            //byte[] receivedBytes = new byte[bytesRead];
            //for ( int i = 0; i < bytesRead; i++ )
            //    receivedBytes[i] = payload.Buffer[i];

            int playerId = payload.Buffer[1]; //BitConverter.ToInt32( receivedBytes, 1 );

            if ( GameManager.Instance.Players.ContainsKey( playerId ) == true )
            {
                PlayerStream ps = GameManager.Instance.Players[playerId];
                Debug.Assert( ps != null && ps.IsWaitingPingResponse == true );

                ps.IsWaitingPingResponse = false;
            }
        }

        private void WaitForPingResponses( object state )
        {
            this.ExecuteRemoveInactivePlayers();
            this.isRemoveInactiveInProgress = false;
            this.removeInActivePlayersTimer.Change( Timeout.Infinite, Timeout.Infinite );
        }

        private void WaitForPingResponses()
        {
            DateTime start = DateTime.Now;

            while ( DateTime.Now.Subtract( start ).Seconds < 3 )
            {
                ;
            }

            this.ExecuteRemoveInactivePlayers();
            this.isRemoveInactiveInProgress = false;
        }
    }

    public class Payload
    {
        public byte[] Buffer
        {
            get;
            set;
        }

        public Stream Stream
        {
            get;
            set;
        }
    }
}
